package hos.java.util;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;

/**
 * <p>Title: NumberUtils </p>
 * <p>Description:  </p>
 * <p>Company: www.mapuni.com </p>
 *
 * @author : 蔡俊峰
 * @version : 1.0
 * @date : 2022/7/2 17:52
 */
public class NumberUtils {

    public static byte[] getBytes(int i) {
        byte[] bInt = new byte[4];
        bInt[3] = (byte) (i % 256);
        int value = i >> 8;
        bInt[2] = (byte) (value % 256);
        value >>= 8;
        bInt[1] = (byte) (value % 256);
        value >>= 8;
        bInt[0] = (byte) (value % 256);
        return bInt;
    }

    //<editor-fold desc="IS">

    public static boolean isNumber(String str) {
        return isCreatable(str);
    }

    /**
     * 1、判断字符串是否为有效的 java 数字，支持16进制、8进制、10进制、正数负数、科学计数法（如8.788006e+05）、类型限定符（110L、3.14f）
     * 2、0X 开头当做 16 进制处理，如 0X89F9；以0开头的非十六进制字符串作为八进制值处理，如 076、-076等
     * 3、注意例如 098 不是八进制，因为8进制是0-7，没有8、9，所以会当做10进制处理，而此时不是数字，所以为false.
     * 4、str 为空或者为 null，都返回 false
     */
    public static boolean isCreatable(String str) {
        if (StringUtils.isEmpty(str)) {
            return false;
        } else {
            char[] chars = str.toCharArray();
            int sz = chars.length;
            boolean hasExp = false;
            boolean hasDecPoint = false;
            boolean allowSigns = false;
            boolean foundDigit = false;
            int start = chars[0] != '-' && chars[0] != '+' ? 0 : 1;
            int i;
            if (sz > start + 1 && chars[start] == '0' && !StringUtils.contains(str, 46)) {
                if (chars[start + 1] == 'x' || chars[start + 1] == 'X') {
                    i = start + 2;
                    if (i == sz) {
                        return false;
                    }

                    while (i < chars.length) {
                        if ((chars[i] < '0' || chars[i] > '9') && (chars[i] < 'a' || chars[i] > 'f') && (chars[i] < 'A' || chars[i] > 'F')) {
                            return false;
                        }

                        ++i;
                    }

                    return true;
                }

                if (Character.isDigit(chars[start + 1])) {
                    for (i = start + 1; i < chars.length; ++i) {
                        if (chars[i] < '0' || chars[i] > '7') {
                            return false;
                        }
                    }

                    return true;
                }
            }

            --sz;

            for (i = start; i < sz || i < sz + 1 && allowSigns && !foundDigit; ++i) {
                if (chars[i] >= '0' && chars[i] <= '9') {
                    foundDigit = true;
                    allowSigns = false;
                } else if (chars[i] == '.') {
                    if (hasDecPoint || hasExp) {
                        return false;
                    }

                    hasDecPoint = true;
                } else if (chars[i] != 'e' && chars[i] != 'E') {
                    if (chars[i] != '+' && chars[i] != '-') {
                        return false;
                    }

                    if (!allowSigns) {
                        return false;
                    }

                    allowSigns = false;
                    foundDigit = false;
                } else {
                    if (hasExp) {
                        return false;
                    }

                    if (!foundDigit) {
                        return false;
                    }

                    hasExp = true;
                    allowSigns = true;
                }
            }

            if (i < chars.length) {
                if (chars[i] >= '0' && chars[i] <= '9') {
                    return true;
                } else if (chars[i] != 'e' && chars[i] != 'E') {
                    if (chars[i] == '.') {
                        return !hasDecPoint && !hasExp ? foundDigit : false;
                    } else if (!allowSigns && (chars[i] == 'd' || chars[i] == 'D' || chars[i] == 'f' || chars[i] == 'F')) {
                        return foundDigit;
                    } else if (chars[i] != 'l' && chars[i] != 'L') {
                        return false;
                    } else {
                        return foundDigit && !hasExp && !hasDecPoint;
                    }
                } else {
                    return false;
                }
            } else {
                return !allowSigns && foundDigit;
            }
        }
    }

    /**
     * 1、检查字符串是否可以解析为数字，即 {@link Integer#parseInt(String)},{@link Long#parseLong(String)}, {@link Float#parseFloat(String),{@link Double#parseDouble(String)}.
     * 2、这个方法可以防止调用上面的方法时出现  {@link java.text.ParseException}
     * 3、注意只支持 10 进制，支持正负数，支持小数，支持不 8进制、16进制、不支持科学计数法，也不支持类型限定符（如 3000L，3.14F）
     */
    public static boolean isParsable(String str) {
        if (StringUtils.isEmpty(str)) {
            return false;
        } else if (str.charAt(str.length() - 1) == '.') {
            return false;
        } else if (str.charAt(0) == '-') {
            return str.length() == 1 ? false : withDecimalsParsing(str, 1);
        } else {
            return withDecimalsParsing(str, 0);
        }
    }

    private static boolean withDecimalsParsing(String str, int beginIdx) {
        int decimalPoints = 0;

        for (int i = beginIdx; i < str.length(); ++i) {
            boolean isDecimalPoint = str.charAt(i) == '.';
            if (isDecimalPoint) {
                ++decimalPoints;
            }

            if (decimalPoints > 1) {
                return false;
            }

            if (!isDecimalPoint && !Character.isDigit(str.charAt(i))) {
                return false;
            }
        }

        return true;
    }

    //</editor-fold>


    //<editor-fold desc="Format">
    public static String format(Double target) {
        return format(target, "#.##");
    }

    public static String format(Double target, String format) {
        try {
            double orEmpty = AnyUtils.asDouble(target);
            DecimalFormat numberFormat = new DecimalFormat(format);
            String result = numberFormat.format(orEmpty);
            if (result == null || result.isEmpty()) {
                return "0";
            }
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return "0";
        }
    }

    public static String format(Float target) {
        return format(target, "#.##");
    }

    public static String format(Float target, String format) {
        try {
            double orEmpty = AnyUtils.asFloat(target);
            DecimalFormat numberFormat = new DecimalFormat(format);
            String result = numberFormat.format(orEmpty);
            if (result == null || result.isEmpty()) {
                return "0";
            }
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return "0";
        }
    }
    //</editor-fold>


    //<editor-fold desc="Create">
    public static Float createFloat(String str) {
        return str == null ? null : Float.valueOf(str);
    }

    public static Double createDouble(String str) {
        return str == null ? null : Double.valueOf(str);
    }

    public static Integer createInteger(String str) {
        return str == null ? null : Integer.decode(str);
    }

    public static Long createLong(String str) {
        return str == null ? null : Long.decode(str);
    }

    public static BigInteger createBigInteger(String str) {
        if (str == null) {
            return null;
        } else {
            int pos = 0;
            int radix = 10;
            boolean negate = false;
            if (str.startsWith("-")) {
                negate = true;
                pos = 1;
            }

            if (!str.startsWith("0x", pos) && !str.startsWith("0X", pos)) {
                if (str.startsWith("#", pos)) {
                    radix = 16;
                    ++pos;
                } else if (str.startsWith("0", pos) && str.length() > pos + 1) {
                    radix = 8;
                    ++pos;
                }
            } else {
                radix = 16;
                pos += 2;
            }

            BigInteger value = new BigInteger(str.substring(pos), radix);
            return negate ? value.negate() : value;
        }
    }

    public static BigDecimal createBigDecimal(String str) {
        if (str == null) {
            return null;
        } else if (StringUtils.isEmpty(str)) {
            throw new NumberFormatException("A blank string is not a valid number");
        } else {
            return new BigDecimal(str);
        }
    }
    //</editor-fold>

    //<editor-fold desc="Max Min">
    public static long min(long... array) {
        long min = array[0];

        for (int i = 1; i < array.length; ++i) {
            if (array[i] < min) {
                min = array[i];
            }
        }

        return min;
    }

    public static int min(int... array) {
        int min = array[0];

        for (int j = 1; j < array.length; ++j) {
            if (array[j] < min) {
                min = array[j];
            }
        }

        return min;
    }

    public static short min(short... array) {
        short min = array[0];

        for (int i = 1; i < array.length; ++i) {
            if (array[i] < min) {
                min = array[i];
            }
        }

        return min;
    }

    public static byte min(byte... array) {
        byte min = array[0];

        for (int i = 1; i < array.length; ++i) {
            if (array[i] < min) {
                min = array[i];
            }
        }

        return min;
    }

    public static double min(double... array) {
        double min = array[0];

        for (int i = 1; i < array.length; ++i) {
            if (Double.isNaN(array[i])) {
                return Double.NaN;
            }

            if (array[i] < min) {
                min = array[i];
            }
        }

        return min;
    }

    public static float min(float... array) {
        float min = array[0];

        for (int i = 1; i < array.length; ++i) {
            if (Float.isNaN(array[i])) {
                return Float.NaN;
            }

            if (array[i] < min) {
                min = array[i];
            }
        }

        return min;
    }

    public static long max(long... array) {
        long max = array[0];

        for (int j = 1; j < array.length; ++j) {
            if (array[j] > max) {
                max = array[j];
            }
        }

        return max;
    }

    public static int max(int... array) {
        int max = array[0];

        for (int j = 1; j < array.length; ++j) {
            if (array[j] > max) {
                max = array[j];
            }
        }

        return max;
    }

    public static short max(short... array) {
        short max = array[0];

        for (int i = 1; i < array.length; ++i) {
            if (array[i] > max) {
                max = array[i];
            }
        }

        return max;
    }

    public static byte max(byte... array) {
        byte max = array[0];

        for (int i = 1; i < array.length; ++i) {
            if (array[i] > max) {
                max = array[i];
            }
        }

        return max;
    }

    public static double max(double... array) {
        double max = array[0];

        for (int j = 1; j < array.length; ++j) {
            if (Double.isNaN(array[j])) {
                return Double.NaN;
            }

            if (array[j] > max) {
                max = array[j];
            }
        }

        return max;
    }

    public static float max(float... array) {
        float max = array[0];

        for (int j = 1; j < array.length; ++j) {
            if (Float.isNaN(array[j])) {
                return Float.NaN;
            }

            if (array[j] > max) {
                max = array[j];
            }
        }

        return max;
    }
    //</editor-fold>


    //<editor-fold desc="compare">
    public static int compare(int x, int y) {
        if (x == y) {
            return 0;
        } else {
            return x < y ? -1 : 1;
        }
    }

    public static int compare(long x, long y) {
        if (x == y) {
            return 0;
        } else {
            return x < y ? -1 : 1;
        }
    }

    public static int compare(short x, short y) {
        if (x == y) {
            return 0;
        } else {
            return x < y ? -1 : 1;
        }
    }

    public static int compare(byte x, byte y) {
        return x - y;
    }
    //</editor-fold>
}
